/*
 * File Name:               XStateMachine.cs
 *
 * Description:             简单的有限状态机
 *                          考虑实现状态机时直接在Owner中创建 而不用继承此类, 这在多个类型的实体中很有优势
 * 思考了一下 状态机的实现还是应该在各自的逻辑中去实现比较合理，一个高层的抽象意义不大
 * Author:                  lisiyu
 * Create Date:             2016/09/23
 */

using System.Collections.Generic;


public abstract class XStateMachine<TOwner, TAction, TMachine> 
    where TAction : XStateAction<TOwner, TAction>
     where TMachine : XStateMachine<TOwner, TAction, TMachine>, new()
{
    protected TAction currentState;
    protected TOwner mOwner;
    protected int mCurrentState;
    protected Dictionary<int, TAction> mStateActions;

    protected virtual void onCreated(TOwner owner)
    {
        mOwner = owner;
        mCurrentState = -1;
        mStateActions = new Dictionary<int, TAction>();
    }

    protected abstract void onAddStates();

    public static TMachine create(TOwner owner)
    {
        TMachine ret = new TMachine();
        ret.onCreated(owner);
        ret.onAddStates();

        return ret;
    }

    public void addState<T>(int stateId) where T : TAction, new()
    {
        if (mStateActions.ContainsKey(mCurrentState))
            return;

        TAction state = new T();
		state.onCreate(mOwner, stateId);
        mStateActions.Add(stateId, state);

        if (currentState == null)
            currentState = state;
    }

    //public virtual bool changeState(int stateId, object param = null)
    //{
    //    if (!mStateActions.ContainsKey(stateId))
    //        return false;

    //    onLeave();

    //    mCurrentState = stateId;
    //    currentState = mStateActions[stateId];

    //    setParam(param);

    //    onEnter();

    //    return true;
    //}

    public virtual bool changeState(int stateId)
    {
        if (!mStateActions.ContainsKey(stateId))
            return false;

        onLeave();

        mCurrentState = stateId;
        currentState = mStateActions[stateId];

        onEnter();

        return true;
    }

    public virtual bool changeState<T>(int stateId, T param = default(T))
    {
        if (!mStateActions.ContainsKey(stateId))
            return false;

        onLeave();

        mCurrentState = stateId;
        currentState = mStateActions[stateId];

        setParam<T>(param);

        onEnter();

        return true;
    }

    //private void setParam(object param)
    //{
    //    if (!mStateActions.ContainsKey(mCurrentState))
    //        return;

    //    mStateActions[mCurrentState].setParam(param);
    //}

    private void setParam<T>(T param)
    {
        if (!mStateActions.ContainsKey(mCurrentState))
            return;

        var state = (mStateActions[mCurrentState] as ISetParam<T>);
        state.setParam(param);
    }

    private void onEnter()
    {
        if (!mStateActions.ContainsKey(mCurrentState))
            return;

        mStateActions[mCurrentState].onEnter();
    }

    private void onLeave()
    {
        if (!mStateActions.ContainsKey(mCurrentState))
            return;

        mStateActions[mCurrentState].onLeave();
    }

    public int getCurrentStateId()
    {
        return mCurrentState;
    }
}